package ws

import (
	"github.com/gorilla/websocket"
	"go.uber.org/zap"
	"net"
	"net/http"
	"wzgames/pkg/logger"
	"wzgames/pkg/utils/netutil"
)

type wsAcceptor struct {
	addr     string
	certfile string
	keyfile  string
	ws       *WebSocket

	upgrader websocket.Upgrader
	listener net.Listener

	httpServ *http.Server
}

func (wa *wsAcceptor) Start() IWebSocketAcceptor {
	var (
		addrObj *netutil.Address
		err     error
		raw     interface{}
	)

	raw, err = netutil.DetectPort(wa.Addr(), func(a *netutil.Address, port int) (interface{}, error) {
		addrObj = a
		return net.Listen("tcp", a.HostPortString(port))
	})

	if err != nil {
		logger.Error("ws.listen failed", zap.String("Name", wa.Name()), zap.Error(err))
		return wa
	}

	wa.listener = raw.(net.Listener)

	mux := http.NewServeMux()
	if addrObj.Path == "" {
		addrObj.Path = "/"
	}

	mux.HandleFunc(addrObj.Path, func(w http.ResponseWriter, r *http.Request) {
		c, e := wa.upgrader.Upgrade(w, r, nil)
		if e != nil {
			return
		}
		ses := newSession(c, wa, netutil.ClientIP(r), nil)
		ses.Start()
	})

	wa.httpServ = &http.Server{Addr: addrObj.HostPortString(wa.Port()), Handler: mux}

	go func() {
		logger.Info("gorilla websocket listen start", zap.String("Name", wa.Name()), zap.String("Addr", addrObj.String(wa.Port())))

		if wa.certfile != "" && wa.keyfile != "" {
			err = wa.httpServ.ServeTLS(wa.listener, wa.certfile, wa.keyfile)
		} else {
			err = wa.httpServ.Serve(wa.listener)
		}
		if err != nil {
			logger.Error("gorilla websocket listen failed", zap.String("Name", wa.Name()), zap.String("Addr", addrObj.String(wa.Port())), zap.Error(err))
		}
	}()

	return wa
}

func (wa *wsAcceptor) Stop() {
	wa.httpServ.Close()
}

func (wa *wsAcceptor) Name() string {
	return "gorillaws.Acceptor"
}

func (wa *wsAcceptor) Addr() string {
	return wa.addr
}

func (wa *wsAcceptor) IsReady() bool {
	return wa.Port() != 0
}

func (wa *wsAcceptor) Port() int {
	if wa.listener == nil {
		return 0
	}
	return wa.listener.Addr().(*net.TCPAddr).Port
}

func (wa *wsAcceptor) Server() *WebSocket {
	return wa.ws
}

func (wa *wsAcceptor) SetCerts(certfile, keyfile string) {
	wa.certfile = certfile
	wa.keyfile = keyfile
}

func (wa *wsAcceptor) SetUpgrader(upgrader interface{}) {
	wa.upgrader = upgrader.(websocket.Upgrader)
}
